﻿using Leo.Awesome.AppPlatform.Domain;
using Leo.Awesome.AppPlatform.Dtos;
using Leo.Awesome.AppPlatform.Enums;
using Leo.Awesome.AppPlatform.Repostories;
using Leo.Awesome.AppPlatform.Services;
using Leo.Core.Application;
using Leo.Core.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;

namespace Leo.Awesome.AppPlatform.Servcies
{
    public class MenuAppService : BaseCrudAppService
        <Menu,
        MenuGetOutPutDto,
        MenuGetListOutputDto,
        MenuPagedResultDto,
        Guid,
        MenuGetListInput,
        MenuCreateUpdateInput,
        MenuCreateUpdateInput,
        MenuGetListPageInput
        >, IMenuAppService
    {
        private readonly IMenuRepository mainRepository;
        private readonly IPermissionGrantAppRepository permissionGrantRepository;

        public MenuAppService(IMenuRepository mainRepository,
             IPermissionGrantAppRepository permissionGrantRepository)
            : base(mainRepository)
        {
            this.mainRepository = mainRepository;
            this.permissionGrantRepository = permissionGrantRepository;
        }

        protected override IQueryable<Menu> BuildQuery(MenuGetListInput getListInput, IQueryable<Menu> query)
        {
            query = query.WhereIf(!getListInput.Name.IsNullOrWhiteSpace(), x => x.Name.Contains(getListInput.Name));
            query = query.OrderBy(x => x.Order);
            return base.BuildQuery(getListInput, query);
        }

        public override async Task<MenuGetOutPutDto> CreateAsync(MenuCreateUpdateInput input)
        {
            await CheckMenuCreateUpdateInput(input);
            await CheckCreatePolicyAsync();
            var entity = await MapToEntityAsync(input);
            if (!input.ParentId.HasValue)
            {
                entity.PathCode = entity.Id.ToString();
            }
            else
            {
                entity.PathCode = await GetNextChildPathCodeAsync(input.ParentId.Value, entity.Id);
            }
            TryToSetTenantId(entity);

            await Repository.InsertAsync(entity, autoSave: true);
            return await MapToGetOutputDtoAsync(entity);
        }

        public override async Task DeleteAsync(Guid id)
        {
            var partMenus = await mainRepository.GetListAsync(x => x.PathCode.Contains(id.ToString()));
            var needDeletePermsions=new List<string>();    
            foreach (var partMenu in partMenus)
            {
                if (partMenu.MenuType == MenuType.Button) 
                {
                    needDeletePermsions.Add(partMenu.Permission);
                }
                await mainRepository.DeleteAsync(partMenu);
            }
            await permissionGrantRepository.DeleteByNameAsync(needDeletePermsions,null);
        }

        public virtual async Task<string> GetNextChildPathCodeAsync(Guid parentId, Guid id)
        {
            var entity = await mainRepository.GetAsync(parentId);
            return entity.PathCode + "/" + id;
        }

        public override async Task<MenuGetOutPutDto> UpdateAsync(Guid id, MenuCreateUpdateInput input)
        {
            await CheckMenuCreateUpdateInput(input);
            return await base.UpdateAsync(id, input);
        }
        private async Task CheckMenuCreateUpdateInput(MenuCreateUpdateInput input)
        {
            CheckBusinessErrors(input == null, "参数错误!");
            CheckBusinessErrors(input.Name.IsNullOrWhiteSpace(), "请输入名称");
            CheckBusinessErrors(input.Code.IsNullOrWhiteSpace(), "请输入编号");
            CheckBusinessErrors(input.Permission.IsNullOrWhiteSpace(), "请输入权限标识");

            var exist = await mainRepository.AnyAsync(x => x.Code == input.Code && x.Id != input.Id);
            CheckBusinessErrors(exist, $"输入的编码【{input.Code}】,已经存在，请重新输入");

            exist = await mainRepository.AnyAsync(x => x.Permission == input.Permission && x.Id != input.Id);
            CheckBusinessErrors(exist, $"输入的权限【{input.Permission}】,已经存在，请重新输入");
        }

        public async Task<List<MenuTreeOutput>> GetTree(MenuTreeInput input)
        {
            var query = await mainRepository.GetQueryableAsync();
            var Menus = new List<Menu>();
            var outputs = new List<MenuTreeOutput>();
            if (input.Name.IsNullOrWhiteSpace() && input.Code.IsNullOrWhiteSpace())
            {
                Menus = query.ToList();
                outputs = ObjectMapper.Map<List<Menu>, List<MenuTreeOutput>>(Menus);
                return outputs.ToTree(x => x.Id, x => x.ParentId).ToList();
            }

            query = query.WhereIf(!input.Name.IsNullOrWhiteSpace(), x => x.Name.Contains(input.Name))
                         .WhereIf(!input.Code.IsNullOrWhiteSpace(), x => x.Code.Contains(input.Code));

            Menus = query.ToList();
            var reversedStrs = new List<Guid>();
            foreach (var item in Menus)
            {
                if (item.PathCode.IsNullOrWhiteSpace())
                {
                    continue;
                }
                var parts = item.PathCode.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);

                var guidList = parts
                     .Select(x => Guid.TryParse(x, out Guid guid) ? guid : Guid.Empty)
                     .ToList();
                reversedStrs.AddRange(guidList);
            }
            Menus = await mainRepository.GetListAsync(x => reversedStrs.Contains(x.Id));
            outputs = ObjectMapper.Map<List<Menu>, List<MenuTreeOutput>>(Menus);
            return outputs.ToTree(x => x.Id, x => x.ParentId).ToList();
        }

        public async Task UpdateMenuEnable(MenuEnableInput input)
        {
            var partMenus = await mainRepository.GetListAsync(x => x.PathCode.Contains(input.Id.ToString()));
            foreach (var item in partMenus)
            {
                item.Display = item.Display;
                await mainRepository.UpdateAsync(item);
            }
        }
    }
}
